#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>
#include "Task.hpp"
#define NUM 6 // 定义线程池中的线程数量

class ThreadPool {
private:
    int num;
    bool stop; // 表示这个进程是否终止
    std::queue<Task> task_queue; // 线程池的任务队列
    pthread_mutex_t lock; // 实现多线程对任务队列的互斥访问
    pthread_cond_t cond; // 条件变量

    // 构造函数，对成员变量进行初始化
    ThreadPool(int _num = NUM):num(_num),stop(false)
    {
        pthread_mutex_init(&lock, nullptr); // 对锁进行初始化，使其处于可用状态
        pthread_cond_init(&cond, nullptr); // 对条件变量进行初始化，使其处于可用状态
    }
    
    // 单例模式需要把拷贝构造给禁了
    ThreadPool(const ThreadPool &){}

    // 这个就是创建出来的单例指针
    static ThreadPool *single_instance;
public:
    // 创建/获取一个单例
    static ThreadPool* getinstance()
    {
        // 如果没有单例，就创建单例，如果有，就直接返回单例指针
        static pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER; // 初始化一把锁，同一时刻只允许一个线程进里面创建单例
        if(single_instance == nullptr){
            pthread_mutex_lock(&_mutex);
            if(single_instance == nullptr){
                // 进入之后需要再判断一下，这是为什么？
                    // 因为创建单例需要时间，如果当A线程进去正在创建单例的时候，B线程走到了第一个语句，此时单例还没创建好，B线程也能进来
                    // 如果我们不加这第二层if判断，那么进来的B线程，在等A线程把锁释放之后，也能进来再创建一个单例
                single_instance = new ThreadPool();// 创建一个ThreadPool对象（创建单例）
                single_instance->InitThreadPool();// 完成对线程池的初始化
            }
            pthread_mutex_unlock(&_mutex);
        }
        return single_instance;// 返回单例指针
    }

    
    // 检测此时任务队列是否为空
    bool TaskQueueIsEmpty()
        {
            return task_queue.size() == 0 ? true : false;
        }
    
    // 访问任务队列之前要先上锁
    void Lock()
    {
        pthread_mutex_lock(&lock);
    }
    // 访问完毕之后再解锁
    void Unlock()
    {
        pthread_mutex_unlock(&lock);
    }
    
    // 任务队列中没有任务时，让线程阻塞等待
    void ThreadWait()
    {
        pthread_cond_wait(&cond, &lock);
        // 1.解锁互斥锁lock：当调用 pthread_cond_wait 时，它会自动释放传入的互斥锁（mutex）。这一操作至关重要，因为若不释放互斥锁，其他线程就无法进入临界区来修改共享资源和触发条件变量，从而可能导致死锁。
        // 2.将当前线程放入条件变量 cond 的等待队列中，然后进入阻塞状态，等待其他线程通过 pthread_cond_signal 或 pthread_cond_broadcast 来唤醒它。
        // 3.当其他线程发出信号唤醒该线程后，pthread_cond_wait 会尝试重新获取之前释放的互斥锁。一旦成功获取互斥锁，函数就会返回，线程可以继续执行后续的代码。
        
    }
    
    // 任务队列中来任务之后，让之前等待的线程别等了，来活了
    void ThreadWakeup()
    {
        // 唤醒之前之前因为 pthread_cond_wait(&cond, &lock);语句而阻塞的进程
        pthread_cond_signal(&cond);
    }
    
    void PushTask(const Task &task) //in
    {
        // 由于是HTTPserver.hpp文件里面调我们，那边没上锁，所以我们里面访问任务队列之前要先上锁
        Lock();
        task_queue.push(task);// 将任务task插入到任务队列中
        Unlock();// 操作完毕再解锁

        ThreadWakeup();// 唤醒之前因为队列里面没有任务而陷入阻塞状态的线程（将该线程的TCB从条件变量cond的阻塞队列中拿出来）
    }
    void PopTask(Task &task) //out
    {
        // 这里因为在ThreadRoutine函数中，PopTask()外面会上锁，所以我里面就不上了
        // 将队列中的第一个任务放入task中（task是一个输出型参数，POP结束之后外面人就能拿到了）
        task = task_queue.front();
        task_queue.pop(); // 拿到之后，我们就把第一个任务删了
    }
    
    static void *ThreadRoutine(void *args)
    {
        // 这个args其实就是指向当前线程池单例的指针（pthread_create语句的第四个参数）
        ThreadPool *tp = (ThreadPool*)args;
        // 这句话就是将指针换个名字，此后tp指向的就是线程池的单例

        while(true){
            Task t;
            tp->Lock();
            
            //如果任务队列为空，我就让线程阻塞等待
            while(tp->TaskQueueIsEmpty()){
                tp->ThreadWait(); 
            }
            // 这里为什么是while语句而不是if语句？
                // 这是为了防止虚假唤醒的问题，确保只有在真正满足从任务队列取任务的条件（也就是任务队列不为空）时才进行后续操作

            // 走到这里说明任务队列中不为空（或者曾经为空，但是后面不为空之后线程被唤醒了），我下面就要从里面拿任务了
            // 注意这时候任务队列的锁还被当前进程持有着呢！也就是说在这期间，别的线程也进不来
            tp->PopTask(t);// 从队列中拿一个任务，放入t中
            tp->Unlock();// 给线程队列解锁
            
            //处理任务t
            t.ProcessOn(); 
        }
    }
    
    bool InitThreadPool()
    {
        for(int i = 0; i < num; i++){
            pthread_t tid;
            if(pthread_create(&tid, nullptr, ThreadRoutine, this) != 0){
                LOG(FATAL, "create thread pool error!");
                return false;
            }
        }
        LOG(INFO, "create thread pool success!");
        return true;
    }
 
    ~ThreadPool()
    {
        pthread_mutex_destroy(&lock);
        pthread_cond_destroy(&cond);
    }
};


// 静态成员变量类外需要初始化
ThreadPool* ThreadPool::single_instance = nullptr;
